/* * Copyright (c) Members of the EGEE Collaboration. 2006-2010. * See http://www.eu-egee.org/partners/ for details on the copyright holders. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id$ */ package org.glite.authz.common.security; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.security.KeyPair; import java.security.PrivateKey; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.X509CertificateObject; import org.bouncycastle.openssl.PEMReader; import org.bouncycastle.openssl.PasswordFinder; import org.glite.authz.common.model.util.Strings; /** * PEM files reader to extract PEM encoded private key and certificates from * file. * <p> * <ul> * <li>OpenSSL 0.9 PCKS1 format compatible * <li>OpenSSL 1.0 PKCS8 format compatible (requires BouncyCastle >= 1.46) * </ul> * * @author Valery Tschopp <valery.tschopp@switch.ch> */ public class PEMFileReader { /** logger */ private Log log= LogFactory.getLog(PEMFileReader.class); static { // add BouncyCastle security provider if not already done if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { Security.addProvider(new BouncyCastleProvider()); } } /** * Default constructor. */ public PEMFileReader() { } /** * Reads the <b>first</b> available PEM encoded private key (PKCS1 and PKCS8 * format) from a filename. * * @param filename * the filename of the file to read from * @param password * the password of the private key if encrypted, can be * <code>null</code> if the key is not encrypted * @return the private key * @throws FileNotFoundException * if the file doesn't exist * @throws IOException * if an error occurs while reading the file */ public PrivateKey readPrivateKey(String filename, String password) throws FileNotFoundException, IOException { File file= new File(filename); return readPrivateKey(file, password); } /** * Reads the <b>first</b> available PEM encoded private key (PKCS1 and PKCS8 * format) from a file object. * * @param file * the file to read from * @param password * the password of the private key if encrypted, can be * <code>null</code> if the key is not encrypted * @return the private key * @throws FileNotFoundException * if the file doesn't exist * @throws IOException * if an error occurs while reading the file */ public PrivateKey readPrivateKey(File file, String password) throws FileNotFoundException, IOException { log.debug("file: " + file); InputStream is= new FileInputStream(file); try { return readPrivateKey(is, password); } catch (IOException ioe) { String error= "Invalid file " + file + ": " + ioe.getMessage(); log.error(error); throw new IOException(error, ioe); } } /** * Reads the <b>first</b> available PEM encoded private key (PKCS1 and PKCS8 * format) from an input stream. * * @param is * the input stream * @param password * the password of the private key if encrypted, can be * <code>null</code> if the key is not encrypted * @return the private key * @throws IOException * if an error occurs while parsing the input stream */ protected PrivateKey readPrivateKey(InputStream is, String password) throws IOException { Reader inputStreamReader= new InputStreamReader(is); PEMReader reader= new PEMReader(inputStreamReader, new PEMPassword(password)); KeyPair keyPair; Object object= null; do { object= reader.readObject(); if (object == null) { String error= "No KeyPair or PrivateKey object found"; log.error(error); throw new IOException(error); } } while (!(object instanceof KeyPair || object instanceof PrivateKey)); log.debug("Object type: " + object.getClass().getCanonicalName()); try { reader.close(); } catch (Exception e) { // ignored } if (object instanceof KeyPair) { keyPair= (KeyPair) object; return keyPair.getPrivate(); } else if (object instanceof PrivateKey) { return (PrivateKey) object; } else { String error= "Unknown object type: " + object.getClass().getName(); log.error(error); throw new IOException(error); } } /** * Reads all PEM encoded X.509 certificates from a file * * @param filename * the filename of the file to read from * @return a list of all X.509 certificates * @throws IOException * if an error occurs while reading the file */ public X509Certificate[] readCertificates(String filename) throws FileNotFoundException, IOException { File file= new File(filename); return readCertificates(file); } /** * Reads all PEM encoded X.509 certificates from a file * * @param file * the file to read from * @return a list of all X.509 certificates * @throws IOException * if an error occurs while reading the file */ public X509Certificate[] readCertificates(File file) throws FileNotFoundException, IOException { FileReader fileReader= new FileReader(file); PEMReader reader= new PEMReader(fileReader, new PEMPassword()); List<X509Certificate> certs= new ArrayList<X509Certificate>(); Object object= null; do { try { // object is null at EOF object= reader.readObject(); if (object instanceof X509CertificateObject) { X509Certificate cert= (X509Certificate) object; certs.add(cert); } } catch (IOException e) { // ignored, trying to read an encrypted object in file, like a // encrypted private key. } } while (object != null); try { reader.close(); } catch (Exception e) { // ignored } return certs.toArray(new X509Certificate[] {}); } /** * PEMPassword is a {@link PasswordFinder} for PEM encoded encrypted private * key or other object. */ private class PEMPassword implements PasswordFinder { /** The password */ private char[] password_= null; /** * Default constructor. The password is <code>null</code>. */ public PEMPassword() { password_= null; } /** * Constructor. * * @param password * the PEM password. */ public PEMPassword(String password) { if (password == null) { password_= null; } else if (Strings.safeTrimOrNullString(password) == null) { password_= null; } else { password_= password.toCharArray(); } } /* * (non-Javadoc) * * @see org.bouncycastle.openssl.PasswordFinder#getPassword() */ public char[] getPassword() { return password_; } } }